package model {
	import view.IPlayerView;
	/**
	 * ...
	 * @author flyingSnow
	 */
	public class Player {
		private var _crntDirection:int=0;
		private var _crntPieces:BitBoard;
		private var _lightingPoints:BitBoard;
		private var _darkPoints:BitBoard;
		
		private var _usedPieces:Array;
		private var _remainNumbers:int;
		
		private var _crntPieceIndex:int;
		private var _gameData:GameData;
		private var _views:Array;
		private var _score:int=0;
		private var _recentlyPiecesUsable:Array;
		private var _hasUsabledPiece:Boolean=true;
		
		public function Player(gameData:GameData) {
			this._gameData = gameData;
			_crntPieces = new BitBoard();
			_lightingPoints = new BitBoard();
			_darkPoints = new BitBoard();
			initUsedPieces();
			_crntPieceIndex = PiecesDefination.PIECES_DEF.length - 2;
			_views = [];
		}
		
		private function initUsedPieces():void {
			_usedPieces = [];
			_remainNumbers = PiecesDefination.PIECES_DEF.length;
			for (var i:int = 0; i < _remainNumbers; i++ ) {
				_usedPieces.push(false);
			}
		}
		
		
		public function get crntPieces():BitBoard {
			return _crntPieces;
		}
		
		public function get lightingPoints():BitBoard {
			return _lightingPoints;
		}
		
		public function get darkPoints():BitBoard {
			return _darkPoints;
		}
		
		public function set darkPoints(value:BitBoard):void {
			_darkPoints = value;
		}
		
		public function putPiece(piece:int, direction:int, column:int, row:int):void {
			var testBB:BitBoard = new BitBoard();
			testBB.putPiece(piece, direction, column, row);
			
			_crntPieces.putPiece(piece, direction, column, row);
			_darkPoints = _crntPieces.extend.sub(_crntPieces);
			_lightingPoints = _lightingPoints.or(testBB.angleExtend).sub(_crntPieces.extend);
			_usedPieces[piece] = true;
			_remainNumbers--;
			
			_score += PiecesDefination.PIECES_NUM[piece];
			if (_remainNumbers == 0) {
				_score += 15;
				if (piece == 0) {
					_score += 5;
				}
			}
		}
		
		public function canPutPiece(piece:int, direction:int, column:int, row:int):BitBoard {
			var testBB:BitBoard = new BitBoard();
			testBB.putPiece(piece, direction, column, row);
			if (!testBB.and(_crntPieces).isZero) {
				return null;
			}
			if (testBB.and(_lightingPoints).isZero) {
				return null;
			}
			if (!testBB.and(_darkPoints).isZero) {
				return null;
			}
			return testBB;
		}
		
		public function startWithPoint(x:int, y:int):void {
			_lightingPoints.setBit(x, y,1);
		}
		
		public function hasUsedPiece(pieceIndex:int):Boolean {
			return _usedPieces[pieceIndex];
		}
		
		public function resetUsableIndex():void {
			for (var i:int = PiecesDefination.PIECES_DEF.length - 1; i >= 0; i-- ) {
				if (getRecentlyUsable(i)) {
					_crntPieceIndex = i;
					return;
				}
			}
			_crntPieceIndex = -1;
		}
		
		public function get lastUsableIndex():int {
			if (remainNumbers > 0) {
				do{
					_crntPieceIndex += 1;
					_crntPieceIndex %= PiecesDefination.PIECES_DEF.length;
				}while (!canUse(_crntPieceIndex)) 
				return _crntPieceIndex;
			}
			return -1;
		}
		public function get nextUsableIndex():int {
			if (remainNumbers > 0) {
				do{
					_crntPieceIndex += PiecesDefination.PIECES_DEF.length-1;
					_crntPieceIndex %= PiecesDefination.PIECES_DEF.length;
				}while (!canUse(_crntPieceIndex)) 
				return _crntPieceIndex;
			}
			return -1;
		}
		
		public function get remainNumbers():int { return _remainNumbers; }
		
		public function get crntPieceIndex():int {
			return _crntPieceIndex;
		}
		public function set crntPieceIndex(value:int):void {
			_crntPieceIndex = value;
		}		
		
		
		public function get crntDirection():int { return _crntDirection; }
		
		public function set crntDirection(value:int):void {
			_crntDirection = value;
		}
		
		public function get score():int {
			return _score;
		}
		
		public function get hasUsabledPiece():Boolean {
			return _hasUsabledPiece;
		}
		
		public function getRecentlyUsable(pieceIndex:int):Boolean {
			return _recentlyPiecesUsable[pieceIndex];
		}
		
		private function canUse(pieceIndex:int):Boolean {
			if (_usedPieces[pieceIndex]) {
				return false;
			}
			
			var tempCanUse:Boolean = false;
			for (var direction:String in PiecesDefination.PIECES_DEF[pieceIndex]) {
				tempCanUse ||= pieceCanBePut(pieceIndex,int(direction));
			}
			return tempCanUse;
		}
		
		public function addView(playerView:IPlayerView):void {
			_views.push(playerView);
			//playerView.update(this);
		}
		
		private function pieceCanBePut(pieceIndex:int,direction:int):Boolean {
			var testBB:BitBoard;
			testBB = new BitBoard();
			testBB.putPiece(pieceIndex, direction, 0, 0);
			
			var height:int = testBB.restrictHeight;
			var width:int = testBB.restrictWidth;						
						
			
			for (var i:int = 0; i < BitBoard.BOARD_WIDTH; i++ ) {
				for (var j:int = 0; j < BitBoard.BOARD_WIDTH; j++ ) {
					testBB = new BitBoard();
					if (!testBB.putPiece(pieceIndex, direction, j, i)) {
						continue;
					}							
					if ((!testBB.and(_lightingPoints).isZero && testBB.and(_gameData.crntPieces).isZero&&testBB.and(_darkPoints).isZero)) {
						return true;
					}
				}
			}
			return false;			
		}
		
		//abstract methold
		public function play():void{}
		
		public function updateViews():void {
			refreshPieceInfo();
			for each(var view:IPlayerView in _views) {
				view["update"](this);
			}
		}
		
		public function getClone(gameData:GameData):Player {
			var cloneData:Player = new Player(gameData);
			cloneData._crntPieces = _crntPieces.clone;
			cloneData._lightingPoints = _lightingPoints.clone;
			cloneData._darkPoints = _darkPoints.clone;
			cloneData._usedPieces = _usedPieces.slice();
			cloneData._remainNumbers = _remainNumbers;
			cloneData._crntPieceIndex = _crntPieceIndex;
			cloneData._score = _score;
			cloneData._recentlyPiecesUsable = _recentlyPiecesUsable.slice();
			cloneData._hasUsabledPiece = _hasUsabledPiece;
			
			return cloneData;
		}
		
		public function get gameData():GameData {
			return _gameData;
		}
		
		private function refreshPieceInfo():void {
			if (!_hasUsabledPiece) {
				return;
			}
			_recentlyPiecesUsable = [];
			_hasUsabledPiece = false;
			for (var i:int = 0; i <= PiecesDefination.PIECES_DEF.length; i++ ) {
				_recentlyPiecesUsable.push(canUse(i));
				if (_recentlyPiecesUsable[i]) {
					_hasUsabledPiece = true;
				}
			}
		}
	}

}